import { PrismaClient } from '@prisma/client';
import type { TransactionClient } from '../types/prisma-types';

interface BatchOptions {
  size?: number;
  timeout?: number;
}

export abstract class BatchService<T> {
  protected items: T[] = [];
  private timer: NodeJS.Timeout | null = null;
  private processing = false;

  constructor(
    protected readonly options: BatchOptions = {},
    protected readonly prisma: PrismaClient
  ) {
    this.options = {
      size: 100,
      timeout: 5000,
      ...options
    };
  }

  protected abstract process(items: T[], tx: TransactionClient): Promise<void>;

  public async add(item: T): Promise<void> {
    this.items.push(item);

    if (this.items.length >= this.options.size!) {
      await this.flush();
    } else if (!this.timer) {
      this.timer = setTimeout(() => this.flush(), this.options.timeout);
    }
  }

  public async flush(): Promise<void> {
    if (this.processing || this.items.length === 0) {
      return;
    }

    this.processing = true;
    const itemsToProcess = [...this.items];
    this.items = [];

    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }

    try {
      await this.prisma.$transaction(async (prismaTransaction: TransactionClient) => {
        await this.process(itemsToProcess, prismaTransaction);
      });
    } catch (error) {
      // 处理失败，将项目放回队列
      this.items = [...itemsToProcess, ...this.items];
      throw error;
    } finally {
      this.processing = false;
    }

    // 检查是否还有剩余项目需要处理
    if (this.items.length > 0) {
      await this.flush();
    }
  }

  public async destroy(): Promise<void> {
    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }

    if (this.items.length > 0) {
      await this.flush();
    }
  }

  public get pendingCount(): number {
    return this.items.length;
  }

  public get isProcessing(): boolean {
    return this.processing;
  }
}
